package se.webbzon.oschi01.worldobject.appearance;

import java.util.List;

import javax.media.j3d.BranchGroup;
import javax.media.j3d.Group;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;

import se.webbzon.oschi01.j3d.AutoOrderedGroup;

public class GroupAppearance3D implements Appearance3D {
        
        // Group appearance properties
        //public static final int EXTENDING             = (1<<0);
        //public static final int CURTAILING            = (1<<1);
        public static final int TRANSPARENCY    = (1<<0);
        
        // The branch group associated with this java3d instance
        private final BranchGroup branchGroup;
                
        // The rendering order number associated with this java3d instance
        private final int renderingOrder;
                
        // The animation of this appearance
        private Animation3D animation;
        
        // A switch used to turn visibility on and off
        //private final Switch switchGroup;
        
        // The group with all child nodes
        //private final Group childGroup;
        
        // The properties of this group appearance
        private final int properties;
        
        /*============================================================
        Constructors
        ============================================================*/
        
        /** Creates a new group appearance from a list of appearances. 
         * The rendering order of this group will be set to zero. **/
        public GroupAppearance3D(List<Appearance3D> appearances) {
                this(0,appearances);
        }
        
        /** Creates a new group appearance from a list of appearances. 
         * The rendering order of this group is given by the input. **/
        public GroupAppearance3D(       int renderingOrder,
                                                                List<Appearance3D> appearances) {
                this(renderingOrder,0,appearances);
        }
        
        /** Creates a new group appearance from a list of appearances with
         * a given set of properties. The rendering order of this group is 
         * given by the input. **/
        public GroupAppearance3D(       int renderingOrder,
                                                                int groupAppearanceProperties,
                                                                List<Appearance3D> appearances) {
                this.renderingOrder = renderingOrder;
                properties = groupAppearanceProperties;
                branchGroup = new BranchGroup();
                branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
                Switch switchGroup = new Switch();
                switchGroup.setCapability(Switch.ALLOW_SWITCH_READ);
                switchGroup.setCapability(Switch.ALLOW_SWITCH_WRITE);
                switchGroup.setWhichChild(Switch.CHILD_ALL);
                TransformGroup transformGroup = new TransformGroup();
                transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
                transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                
                final boolean   transparency = isSet(properties,TRANSPARENCY);
                                                //curtailing = isSet(properties,CURTAILING);
                
                // Create auto-ordered group
                AutoOrderedGroup aog;
                if (transparency)
                        aog = new AutoOrderedGroup(); 
                else
                        aog = null;
                
                // Add appearances to child group
                if (appearances != null) {
                        for (Appearance3D appearance : appearances) {
                                BranchGroup child = appearance.getJ3dBranch();

                                if (child != null) {    
                                        // Add node to child group
                                        if (transparency)
                                                aog.addChild(child, appearance.getRenderingOrder());
                                        else
                                                transformGroup.addChild(child);
                                }
                        }
                }
                
                // Attach to structure
                if (transparency)
                        transformGroup.addChild(aog);
                switchGroup.addChild(transformGroup);
                branchGroup.addChild(switchGroup);
        }
        
        /*============================================================
        Public Methods
        ============================================================*/

        /** Adds an appearance to the appearance group. **/
        public boolean addAppearance(Appearance3D appearance) {
                //if (isSet(properties,EXTENDING)) { 
                        // Retrieve the node of the appearance
                        BranchGroup child = appearance.getJ3dBranch();
                        if (child == null)
                                return true;
                        
                        Group g = (Group) branchGroup.getChild(0);
                        TransformGroup tg = (TransformGroup) g.getChild(0);
                        
                        // Create a new branch group for the node
                        //BranchGroup branchGroup = new BranchGroup();
                        //branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
                        
                        // Add the node the group
                        //branchGroup.addChild(childNode);
                        if (isSet(properties,TRANSPARENCY))
                                ((AutoOrderedGroup) tg.getChild(0)).
                                        addChild(child,appearance.getRenderingOrder());
                        else
                                tg.addChild(child);
                        return true;
                //}
                //return false;
        }
        
        /** Removes an appearance from the appearance group. **/
        public boolean removeAppearance(Appearance3D appearance) {
                //if (isSet(properties,CURTAILING)) {
                        // Retrieve the node of the appearance
                        BranchGroup child = appearance.getJ3dBranch();
                        if (child == null)
                                return true;
                        
                        Group g = (Group) branchGroup.getChild(0);
                        TransformGroup tg = (TransformGroup) g.getChild(0);
                        
                        // Remove the node and the branch group from the group
                        /*BranchGroup branchGroup = (BranchGroup) childNode.getParent();
                        if (branchGroup.getParent() == childGroup) {
                                childGroup.removeChild(branchGroup);
                                branchGroup.removeChild(childNode);
                                return true;
                        }*/
                        if (isSet(properties,TRANSPARENCY))
                                ((Group) tg.getChild(0)).removeChild(child);
                        else
                                tg.removeChild(child);
                        return true;
                //}
                //return false;
        }
        
        @Override public boolean getVisible() {
                Switch switchGroup = (Switch) branchGroup.getChild(0);
                return (switchGroup.getWhichChild() == Switch.CHILD_ALL);
        }

        @Override public void setVisible(boolean visible) {
                Switch switchGroup = (Switch) branchGroup.getChild(0);
                if (visible)
                        switchGroup.setWhichChild(Switch.CHILD_ALL);
                else
                        switchGroup.setWhichChild(Switch.CHILD_NONE);
        }
        
        @Override public void onStepEnd() {
                if (animation != null)
                        animation.onStepEnd(this);
        }

        @Override public void setAnimation(Animation3D animation) {
                this.animation = animation;
        }

        @Override public Animation3D getAnimation() {
                return animation;
        }

        @Override public void getTransform(Transform3D transform) {
                Group g = (Group) branchGroup.getChild(0);
                TransformGroup tg = (TransformGroup) g.getChild(0);
                tg.getTransform(transform);
        }

        @Override public void setTransform(Transform3D transform) {
                Group g = (Group) branchGroup.getChild(0);
                TransformGroup tg = (TransformGroup) g.getChild(0);
                tg.setTransform(transform);
        }

        @Override public int getRenderingOrder() {
                return renderingOrder;
        }

        @Override public BranchGroup getJ3dBranch() {
                return branchGroup;
        }
        
        /*============================================================
        Private Methods
        ============================================================*/
        
        /** Returns true if the given property is set in the given properties. **/
        private static final boolean isSet(int properties, int property) {
                return ((properties & property) == property);
        }
        
}